#include "ring_renderer.h"

RingRenderBatcher::RingRenderBatcher(CL_GraphicContext &gc, CL_String program_name, CL_ResourceManager *resources)
: cur_position(0)
{
#ifdef USE_OPENGL_2
	program = create_shader_program(gc, program_name, resources);
#endif
}


void RingRenderBatcher::draw_ring(CL_GraphicContext &gc, const CL_Rectf &size, const CL_Rect &frame, const CL_Texture &texture, float x, float y, float r, float s, float sa, CL_Colorf c)
{
	gc.set_batcher(this);
	if (current_texture != texture)
	{
		flush(gc);
		current_texture = texture;
	}

	CL_Vec4f color(c.r, c.g, c.b, c.a);

	CL_Rectf text_pos(frame.left / size.get_width(), frame.top / size.get_height(), frame.right / size.get_width(), frame.bottom / size.get_height());
	float pixel_size = s / frame.get_width();
	float len = 3.14159265f * 2.0f * r / pixel_size;
	float da = 360.0f / len;
	int tex_p = 0;
	int tex_len = (int)frame.get_height();
	float a = 0;
	while (a < 360.0f)
	{
		float c1 = cos(CL_Angle(sa + a, cl_degrees).to_radians());
		float c2 = cos(CL_Angle(sa + a+da, cl_degrees).to_radians());
		float s1 = sin(CL_Angle(sa + a, cl_degrees).to_radians());
		float s2 = sin(CL_Angle(sa + a+da, cl_degrees).to_radians());

		positions[cur_position+0] = CL_Vec2f( x + c1 * r, y + s1 * r );
		positions[cur_position+1] = CL_Vec2f( x + c1 * (r + s), y + s1 * (r + s) );
		positions[cur_position+2] = CL_Vec2f( x + c2 * (r + s), y + s2 * (r + s) );
		positions[cur_position+3] = CL_Vec2f( x + c2 * (r), y + s2 * (r) );
		positions[cur_position+4] = CL_Vec2f( x + c2 * (r + s), y + s2 * (r + s) );
		positions[cur_position+5] = CL_Vec2f( x + c1 * r, y + s1 * r );

		tex1_coords[cur_position+0] = CL_Vec2f( text_pos.left, text_pos.top + ((float)tex_p) / ((float)tex_len) );
		tex1_coords[cur_position+1] = CL_Vec2f( text_pos.right, text_pos.top + ((float)tex_p) / ((float)tex_len) );
		tex1_coords[cur_position+2] = CL_Vec2f( text_pos.right, text_pos.top + (1.0f / ((float)tex_len)) + ((float)tex_p) / ((float)tex_len) );
		tex1_coords[cur_position+3] = CL_Vec2f( text_pos.left, text_pos.top + (1.0f / ((float)tex_len)) + ((float)tex_p) / ((float)tex_len) );
		tex1_coords[cur_position+4] = CL_Vec2f( text_pos.right, text_pos.top + (1.0f / ((float)tex_len)) + ((float)tex_p) / ((float)tex_len) );
		tex1_coords[cur_position+5] = CL_Vec2f( text_pos.left, text_pos.top + ((float)tex_p) / ((float)tex_len) );

		colors[cur_position+0] = color;
		colors[cur_position+1] = color;
		colors[cur_position+2] = color;
		colors[cur_position+3] = color;
		colors[cur_position+4] = color;
		colors[cur_position+5] = color;

		cur_position += 6;

		if (cur_position == num_vertices)
			flush(gc);

		a += da;
		tex_p = (tex_p++) % tex_len;
	}
}


void RingRenderBatcher::flush(CL_GraphicContext &gc)
{
	if (cur_position > 0)
	{
		CL_PrimitivesArray primarray(gc);
		primarray.set_attributes(0, positions);
		primarray.set_attributes(1, colors);
		primarray.set_attributes(2, tex1_coords);

		gc.set_texture(0, current_texture);
#ifdef USE_OPENGL_2
		gc.set_program_object(program, cl_program_matrix_modelview_projection);
#else
		gc.set_program_object(cl_program_single_texture);
#endif
		gc.draw_primitives(cl_triangles, cur_position, primarray);
		gc.reset_program_object();
		gc.reset_texture(0);

		cur_position = 0;
		current_texture = CL_Texture();
	}
}


void RingRenderBatcher::modelview_changed(const CL_Mat4f &modelview)
{
}

CL_ProgramObject RingRenderBatcher::create_shader_program(CL_GraphicContext &gc, CL_String resource_id, CL_ResourceManager *resources)
{
	CL_ProgramObject program = CL_ProgramObject::load(gc, resource_id, resources);
	if (!program.link())
		throw CL_Exception("Unable to link program");
	return program;
}




RingSprite::RingSprite()
{
}

RingSprite::RingSprite(CL_GraphicContext &gc, RingRenderBatcher *batcher_, const CL_String &name, CL_ResourceManager *resources)
: batcher(batcher_), cur_frame(0)
{
	CL_DomElement resource = resources->get_resource(name).get_element();
	CL_DomNode image_node = resource.select_node("image");
	if (!image_node.is_element())
		throw CL_Exception(cl_format("Resource '%1' has no image file specified!", name));
	CL_DomElement image_el = image_node.to_element();
	if (!image_el.has_attribute("file"))
		throw CL_Exception(cl_format("Resource '%1' has no image file specified!", name));
	CL_PixelBuffer image = CL_ImageProviderFactory::load(image_el.get_attribute("file"), resources->get_directory(resources->get_resource(name)));
	geometry = CL_Rect(image.get_size());
	texture = CL_Texture(gc, image.get_size());
	texture.set_image(image);
	CL_DomNode grid_node = image_node.select_node("grid");
	if (grid_node.is_element())
	{
		CL_DomElement grid_el = grid_node.to_element();
		int xarray = 1;
		int yarray = 1;
		int width = 0;
		int height = 0;
		std::vector<CL_String> image_size = CL_StringHelp::split_text(grid_el.get_attribute("size"), ",");
		if (image_size.size() > 0)
			width = CL_StringHelp::text_to_int(image_size[0]);
		if (image_size.size() > 1)
			height = CL_StringHelp::text_to_int(image_size[1]);
		if (grid_el.has_attribute("array"))
		{
			std::vector<CL_String> image_array = CL_StringHelp::split_text(grid_el.get_attribute("array"), ",");
			if (image_array.size() == 2)
			{
				xarray = CL_StringHelp::text_to_int(image_array[0]);
				yarray = CL_StringHelp::text_to_int(image_array[1]);
			}
			else
			{
				throw CL_Exception(cl_format("Resource '%1' has incorrect array attribute, must be \"X,Y\"!", name));
			}
		}
		int ystart = 0;
		for (int y = 0; y < yarray; y++)
		{
			int xstart = 0;
			for (int x = 0; x < xarray; x++)
			{
				if(xstart + width > image.get_width() || ystart + height > image.get_height())
					throw CL_Exception("loading ring sprite : Outside pixelbuffer bounds");
				frames.push_back(CL_Rect(xstart, ystart, xstart + width, ystart + height));
				cl_log_event("Debug", "Ring frame added");
				xstart += width;
			}
			ystart += height;
		}
	}
	else
	{
		frames.push_back(geometry);
	}
}

RingSprite::~RingSprite()
{
}

void RingSprite::set_frame(int f)
{
	cur_frame = f % frames.size();
}

void RingSprite::draw_ring(CL_GraphicContext &gc, float x, float y, float r, float s, float sa, CL_Colorf color)
{
	batcher->draw_ring(gc, geometry, frames[cur_frame], texture, x, y, r, s, sa, color);
}
